package org.bt.common.exception;

import cn.hutool.json.JSONUtil;
import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import lombok.extern.slf4j.Slf4j;
import org.bt.common.constant.MessageConstant;
import org.bt.common.entity.Error;
import org.bt.common.enums.ResponseStatus;
import org.bt.common.utils.AppUtil;
import org.bt.common.utils.MessageSourceUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;

/**
 * HttpRequest请求Sentinel异常处理
 *
 * @author tongw
 * @date 2021/9/1
 */
@Component
@Slf4j
public class SentinelBlockExceptionHandler implements BlockExceptionHandler {

    @Autowired
    protected MessageSourceUtil messageSource;

    public SentinelBlockExceptionHandler(){
    }

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
        response.setContentType("application/json;charset=utf-8");
        response.setStatus(Integer.parseInt(ResponseStatus.TOO_MANY_REQUESTS.value()));
        Error error = new Error();
        if (e instanceof FlowException) {
            error.setCode(MessageConstant.ErrorCode.Sentinel.SENTINEL_ERR_L_FLOW);
            error.setMessage(messageSource.getMessage(MessageConstant.ErrorCode.Sentinel.SENTINEL_ERR_L_FLOW,
                    new Object[]{request.getRemoteHost()}));
            error.setDetails(e.getRule().toString());
        } else if (e instanceof DegradeException) {
            error.setCode(MessageConstant.ErrorCode.Sentinel.SENTINEL_ERR_L_DEGRADE);
            error.setMessage(messageSource.getMessage(MessageConstant.ErrorCode.Sentinel.SENTINEL_ERR_L_DEGRADE,
                    new Object[]{request.getRemoteHost()}));
            error.setDetails(e.getRule().toString());
        } else if (e instanceof ParamFlowException) {
            error.setCode(MessageConstant.ErrorCode.Sentinel.SENTINEL_ERR_L_PARAM_FLOW);
            error.setMessage(messageSource.getMessage(MessageConstant.ErrorCode.Sentinel.SENTINEL_ERR_L_PARAM_FLOW,
                    new Object[]{request.getRemoteHost()}));
            error.setDetails(e.getRule().toString());
        } else if (e instanceof SystemBlockException) {
            error.setCode(MessageConstant.ErrorCode.Sentinel.SENTINEL_ERR_L_SYSTEM_BLOCK);
            error.setMessage(messageSource.getMessage(MessageConstant.ErrorCode.Sentinel.SENTINEL_ERR_L_SYSTEM_BLOCK,
                    new Object[]{request.getRemoteHost()}));
            error.setDetails(e.getRule().toString());
        } else if (e instanceof AuthorityException) {
            error.setCode(MessageConstant.ErrorCode.Sentinel.SENTINEL_ERR_L_AUTHORITY);
            error.setMessage(messageSource.getMessage(MessageConstant.ErrorCode.Sentinel.SENTINEL_ERR_L_AUTHORITY,
                    new Object[]{request.getRemoteHost()}));
            error.setDetails(e.getRule().toString());
        } else {
            error.setCode(MessageConstant.ErrorCode.Common.COMMON_ERR_L_UNKNOWN);
            error.setMessage(messageSource.getMessage(MessageConstant.ErrorCode.Common.COMMON_ERR_L_UNKNOWN));
            error.setDetails(AppUtil.printFullExceptionStack(e));
        }

        PrintWriter out = response.getWriter();
        out.print(JSONUtil.toJsonStr(error));
        out.flush();
        out.close();
    }
}
